package com.one.blocks.mvc.exception;

import com.one.blocks.common.enums.ResponseEnum;
import com.one.blocks.common.exception.BusinessException;
import com.one.blocks.common.response.ResponseHelper;
import com.one.blocks.common.response.WebResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.validation.ValidationException;

/**
 * ResponseEntityExceptionHandler返回的是text/plain;charset=UTF-8，不方便统一json处理
 *
 * @author linfuxin Created on 2022-02-14 16:22:21
 * @see org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler
 */
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 未捕获异常
     *
     * @param e Exception
     * @return 统一格式异常响应
     */
    @ExceptionHandler(Exception.class)
    public WebResponse<Void> unknown(Exception e) {
        log.error("未知异常!", e);
        return ResponseHelper.fail(ResponseEnum.SERVICE_ERROR);
    }

    /**
     * 系统业务异常
     *
     * @param e Exception
     * @return 统一格式异常响应
     */
    @ExceptionHandler(BusinessException.class)
    public WebResponse<Void> bizException(BusinessException e) {
        log.error("业务异常!", e);
        return ResponseHelper.fail(e.getErrorEnum());
    }

    /**
     * 与@Validated相关,controller非bean参数校验异常
     *
     * @param e ValidationException
     * @return 统一格式异常响应
     */
    @ExceptionHandler(ValidationException.class)
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public WebResponse<Void> handValidationException(ValidationException e) {
        return ResponseHelper.fail(HttpStatus.BAD_REQUEST.value(), e.getMessage());
    }

    /**
     * @param e Exception
     * @return 统一格式异常响应
     */
    @ExceptionHandler({
            BindException.class,
            MissingServletRequestParameterException.class,
            ServletRequestBindingException.class,
            TypeMismatchException.class,
            HttpMessageNotReadableException.class,
            MethodArgumentNotValidException.class,
            MissingServletRequestPartException.class,
    })
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public WebResponse<Void> handleBindException(Exception e) {
        String msg = e instanceof BindException ? resolveErrorMessage((BindException) e) : e.getMessage();
        return ResponseHelper.fail(HttpStatus.BAD_REQUEST.value(), msg);
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(value = HttpStatus.NOT_FOUND)
    public WebResponse<Void> handNoHandlerFoundException(NoHandlerFoundException e) {
        return ResponseHelper.fail(HttpStatus.NOT_FOUND.value(), e.getMessage());
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(value = HttpStatus.METHOD_NOT_ALLOWED)
    public WebResponse<Void> handNotSupportedException(HttpRequestMethodNotSupportedException e) {
        return ResponseHelper.fail(HttpStatus.METHOD_NOT_ALLOWED.value(), e.getMessage());
    }

    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    @ResponseStatus(value = HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    public WebResponse<Void> handMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
        return ResponseHelper.fail(HttpStatus.UNSUPPORTED_MEDIA_TYPE.value(), e.getMessage());
    }

    @ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
    @ResponseStatus(value = HttpStatus.NOT_ACCEPTABLE)
    public WebResponse<Void> handMediaTypeNotAcceptableException(HttpMediaTypeNotAcceptableException e) {
        return ResponseHelper.fail(HttpStatus.NOT_ACCEPTABLE.value(), e.getMessage());
    }

    private String resolveErrorMessage(BindException e) {
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        if (log.isDebugEnabled()) {
            log.debug(objectError.toString());
        }
        StringBuilder errorMessage = new StringBuilder();

        if (objectError instanceof FieldError) {
            FieldError fieldError = (FieldError) objectError;
            errorMessage.append(fieldError.getField());
        }

        errorMessage.append(objectError.getDefaultMessage());
        return errorMessage.toString();
    }
}
